﻿#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.IO;
using System.Text;
using Yarr;

namespace Yarr.YarrPad
{
    internal sealed class CommandResponse
    {
        private object result;
        private string output;
        private string error;

        public object Result
        {
            get { return result; }
            set { result = value; }
        }

        public String Output
        {
            get { return output; }
            set { output = value; }
        }

        public String Error
        {
            get { return error; }
            set { error = value; }
        }
    }

    internal sealed class CommandBuffer
    {
        Yarr.Environment environment;
        private readonly StringBuilder commandText;

        private StreamWriter outputWriter;
        private MemoryStream outputStream;

        private StreamWriter errorWriter;
        private MemoryStream errorStream;

        public CommandBuffer(Yarr.Environment environment, 
                                StreamWriter outputWriter,
                                MemoryStream outputStream,
                                StreamWriter errorWriter,
                                MemoryStream errorStream)
        {
            if (environment == null)
            {
                throw new ArgumentNullException("environment");
            }

            this.outputWriter = outputWriter;
            this.outputStream = outputStream;

            this.errorWriter = errorWriter;
            this.errorStream = errorStream;

            this.environment = environment;
            
            commandText = new StringBuilder();
        }

        public Environment Environment
        {
            get { return environment; }
            set { environment = value; }
        }

        public string Text
        {
            get { return commandText.ToString(); }
        }

        public CommandResponse Add(string text)
        {
            commandText.Append(text);
            string command = commandText.ToString().Trim();

            if (Runtime.IsStatementComplete(command, environment))
            {
                try
                {
                    return DoCommand(command, environment);
                }
                finally
                {
                    commandText.Length = 0;
                }
            }

            return null;
        }

        public void Clear()
        {
            commandText.Length = 0;
        }


        private CommandResponse DoCommand(string command, Environment env)
        {
            try
            {
                return BuildResponse(Runtime.EvalString(command, environment));
            }
            catch (Exception ex)
            {
                StringBuilder exception = new StringBuilder();
                exception.Append(ex.Message);
                if (exception.Length > 0)
                    exception.Append(System.Environment.NewLine);

                System.Console.Error.WriteLine(exception.ToString());

                return BuildResponse(null);
            }
        }

        private CommandResponse BuildResponse(Object result)
        {
            string output = String.Empty;

            if (outputWriter != null && outputStream != null)
            {
                outputWriter.Flush();

                lock (outputStream)
                {
                    if (outputStream.Length > 0)
                    {
                        output = outputWriter.Encoding.GetString(outputStream.ToArray());
                        outputStream.SetLength(0);
                    }
                }
            }

            string error = String.Empty;

            if (errorWriter != null && errorStream != null)
            {
                errorWriter.Flush();

                lock (errorStream)
                {
                    if (errorStream.Length > 0)
                    {
                        error = errorWriter.Encoding.GetString(errorStream.ToArray());
                        errorStream.SetLength(0);
                    }
                }
            }

            return new CommandResponse
                {
                    Result = result,
                    Output = output,
                    Error = error
                };
        }
    }
}